package com.moss.cloud.base.biz.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.moss.cloud.base.api.model.from.BaseFrom;
import com.moss.cloud.base.api.model.from.DeptFrom;
import com.moss.cloud.base.api.model.param.DeptParam;
import com.moss.cloud.base.biz.mapper.SysDeptMapper;
import com.moss.cloud.base.biz.service.ISysDeptService;
import com.moss.cloud.base.biz.utils.DataProUtil;
import com.moss.cloud.common.base.model.po.SysDept;
import com.moss.cloud.common.base.utils.PageUtil;
import com.moss.cloud.common.core.constant.Constant;
import com.moss.cloud.common.core.enums.state.Deleted;
import com.moss.cloud.common.core.exception.SystemErrorType;
import com.moss.cloud.common.core.model.vo.BaseProTree;
import com.moss.cloud.common.core.model.vo.DeptTreeVO;
import com.moss.cloud.common.core.utils.BooleanHandel;
import com.moss.cloud.common.core.utils.TreeUtil;
import com.moss.cloud.common.core.utils.UserContextHolder;
import com.moss.cloud.common.security.annotation.RequiresDept;
import com.moss.cloud.common.security.annotation.RequiresTenant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 部门业务实现类
 *
 * @author 瑾年
 * @date 2023年4月9日
 */
@Slf4j
@Service
public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> implements ISysDeptService {

    /**
     * 根据ID查找部门信息
     *
     * @param deptId
     * @return
     */
    @Override
    public SysDept findById(Long deptId) {
        return this.getById(deptId);
    }

    /**
     * 根据当前部门ID查找当前部门及其子部门
     *
     * @return
     */
    @Override
    public List<SysDept> sysDepts(Long deptId) {
        List<SysDept> subSysDept = new ArrayList<>();
        List<SysDept> sysDepts = this.list(new QueryWrapper<SysDept>().lambda().eq(SysDept::getParentId, deptId)
                .notIn(SysDept::getDeleted, Deleted.DELETE.code()));
        node(sysDepts, subSysDept);
        subSysDept.addAll(CollectionUtils.isNotEmpty(sysDepts) ? sysDepts : Collections.emptyList());
        SysDept sysDept = this.findById(deptId);
        BooleanHandel.trueThrow(Objects.isNull(sysDept)).throwMessage(SystemErrorType.DATE_EMPTY);
        subSysDept.add(sysDept);
        return subSysDept.stream().sorted(Comparator.comparing(SysDept::getId)).collect(Collectors.toList());
    }

    /**
     * 查找子部门
     *
     * @param sysDepts   当前部门的子部门
     * @param subSysDept 所有部门
     */
    private void node(List<SysDept> sysDepts, List<SysDept> subSysDept) {
        BooleanHandel.isTrue(CollectionUtils.isNotEmpty(sysDepts)).trueHandle(() -> sysDepts.forEach(item -> {
            List<SysDept> sysDeptsList = this.list(new QueryWrapper<SysDept>().lambda().eq(SysDept::getParentId, item.getId())
                    .notIn(SysDept::getDeleted, Deleted.DELETE.code()));
            subSysDept.addAll(sysDeptsList);
            node(sysDeptsList, subSysDept);
        }));
    }

    /**
     * 是否为当前用户的部门以及下属部门
     *
     * @param userDeptId
     * @param deptId
     * @return
     */
    @Override
    public Boolean isUserSubDept(Long userDeptId, Long deptId) {
        List<SysDept> sysDeptList = this.sysDepts(userDeptId);
        List<Long> deptIds = DataProUtil.deptIds(sysDeptList);
        return deptIds.contains(deptId);
    }

    /**
     * 租户部门树查询
     *
     * @param deptParam
     * @return
     */
    @Override
    @RequiresDept
    @RequiresTenant
    public List<BaseProTree> deptTree(DeptParam deptParam) {
        List<SysDept> sysDepts = this.sysDepts(deptParam).getRecords();
        List<BaseProTree> baseProTrees = CollectionUtils.isNotEmpty(sysDepts) ? sysDepts.stream().map(item -> {
            BaseProTree baseProTree = new BaseProTree();
            baseProTree.setKey(item.getId());
            baseProTree.setParentId(item.getParentId());
            baseProTree.setTitle(item.getName());
            baseProTree.setValue(item.getId());
            baseProTree.setLabel(item.getName());
            return baseProTree;
        }).collect(Collectors.toList()) : Collections.emptyList();
        return TreeUtil.toTree(baseProTrees, 0L, BaseProTree::getKey, BaseProTree::getParentId, BaseProTree::setChildren);
    }

    /**
     * 租户部门树全量数据查询
     *
     * @param deptParam
     * @return
     */
    @Override
    @RequiresDept
    @RequiresTenant
    public IPage<DeptTreeVO> allDeptTree(DeptParam deptParam) {
        IPage<SysDept> sysDepts = this.sysDepts(deptParam);
        IPage<DeptTreeVO> treePage = new Page<>();
        List<DeptTreeVO> deptTrees = CollectionUtils.isNotEmpty(sysDepts.getRecords()) ? sysDepts.getRecords().stream().map(item -> {
            DeptTreeVO deptTreeVO = new DeptTreeVO();
            BeanUtils.copyProperties(item, deptTreeVO);
            return deptTreeVO;
        }).collect(Collectors.toList()) : Collections.emptyList();
        BeanUtils.copyProperties(sysDepts, treePage);
        List<DeptTreeVO> tree = TreeUtil.toTree(deptTrees, 0L, DeptTreeVO::getId, DeptTreeVO::getParentId, DeptTreeVO::setChildren);
        treePage.setRecords(tree);
        return treePage;
    }

    private IPage<SysDept> sysDepts(DeptParam deptParam) {
        Long tenantId = UserContextHolder.getTenantId();
        Page<SysDept> page = PageUtil.createPage(deptParam.getCurrent(), Constant.MAX_VALUE);
        return this.page(page, new QueryWrapper<SysDept>().lambda().eq(SysDept::getTenantId, tenantId)
                .like(StringUtils.isNotBlank(deptParam.getName()), SysDept::getName, deptParam.getName())
                .notIn(SysDept::getDeleted, Deleted.DELETE.code()).orderByAsc(SysDept::getOrderNum));
    }

    /**
     * 新增部门
     *
     * @param deptFrom
     * @return
     */
    @Override
    public boolean saveDept(DeptFrom deptFrom) {
        TreeUtil.checkSort(deptFrom.getOrderNum());
        this.checkDeptSaveForm(deptFrom);
        SysDept sysDept = new SysDept();
        BeanUtils.copyProperties(deptFrom, sysDept);
        sysDept.setTenantId(UserContextHolder.getTenantId());
        return this.save(sysDept);
    }

    /**
     * 校验部门新增参数
     *
     * @param deptFrom
     */
    private void checkDeptSaveForm(DeptFrom deptFrom) {
        BooleanHandel.trueThrow(this.checkDeptNameExist(deptFrom.getParentId(), deptFrom.getName(), deptFrom.getLevel())).throwMessage(SystemErrorType.DEPT_NAME_EXIST);
    }

    private boolean checkDeptNameExist(Long parentId, String name, Integer level) {
        QueryWrapper<SysDept> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .select(SysDept::getId)
                .eq(SysDept::getParentId, parentId)
                .eq(SysDept::getLevel, level)
                .eq(SysDept::getName, name)
                .ne(SysDept::getDeleted, Deleted.DELETE.code());
        long count = this.count(queryWrapper);
        return Constant.ZERO < count;
    }

    /**
     * 更新部门
     *
     * @param deptFrom
     * @return
     */
    @Override
    public boolean updateDept(DeptFrom deptFrom) {
        TreeUtil.checkSort(deptFrom.getOrderNum());
        this.checkDeptUpdateForm(deptFrom);
        BooleanHandel.trueThrow(Objects.isNull(deptFrom.getId())).throwMessage(SystemErrorType.PRIMARY_KEY);
        this.checkDeptData(deptFrom.getId());
        SysDept dept = new SysDept();
        BeanUtils.copyProperties(deptFrom, dept);
        return this.updateById(dept);
    }

    /**
     * 校验部门更新参数
     *
     * @param deptFrom
     */
    private void checkDeptUpdateForm(DeptFrom deptFrom) {
        BooleanHandel.trueThrow(checkDeptNameExistExcludeSelf(deptFrom.getId(), deptFrom.getParentId(), deptFrom.getLevel(), deptFrom.getName())).throwMessage(SystemErrorType.DEPT_NAME_EXIST);
    }

    /**
     * 校验部门名称是否存在（排除自己）
     *
     * @param deptId
     * @param parentId
     * @param level
     * @param name
     * @return
     */
    private boolean checkDeptNameExistExcludeSelf(Long deptId, Long parentId, Integer level, String name) {
        QueryWrapper<SysDept> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .select(SysDept::getId)
                .ne(SysDept::getId, deptId)
                .eq(SysDept::getParentId, parentId)
                .eq(SysDept::getLevel, level)
                .eq(SysDept::getName, name)
                .ne(SysDept::getDeleted, Deleted.DELETE.code());
        long count = this.count(queryWrapper);
        return Constant.ZERO < count;
    }

    /**
     * 删除部门
     *
     * @param baseFrom
     * @return
     */
    @Override
    public boolean removeDept(BaseFrom baseFrom) {
        this.checkDeptData(baseFrom.getId());
        List<SysDept> sysDepts = this.list(new QueryWrapper<SysDept>().lambda().eq(SysDept::getParentId, baseFrom.getId()));
        BooleanHandel.trueThrow(CollectionUtils.isNotEmpty(sysDepts)).throwMessage(SystemErrorType.CHILDREN_NOT_EMPTY);
        return this.removeById(baseFrom.getId());
    }

    /**
     * 数据验证，数据权限验证
     *
     * @param deptId
     */
    private void checkDeptData(Long deptId) {
        Long userDeptId = UserContextHolder.getDeptId();
        Long tenantId = UserContextHolder.getTenantId();
        SysDept sysDept = this.findById(deptId);
        BooleanHandel.trueThrow(Objects.isNull(sysDept)).throwMessage(SystemErrorType.DATE_EMPTY);
        BooleanHandel.trueThrow(!tenantId.equals(sysDept.getTenantId())).throwMessage(SystemErrorType.NOT_BELONG_TENANT);
        Boolean subDept = this.isUserSubDept(userDeptId, deptId);
        BooleanHandel.trueThrow(subDept.equals(Boolean.FALSE)).throwMessage(SystemErrorType.DEPT_NOT_BELONG_USER);
    }
}
